package org.cn.osiris.user.service.impl;

import com.alibaba.fastjson.JSONObject;
import org.cn.osiris.basic.dto.LoginDto;
import org.cn.osiris.basic.exception.BusinessException;
import org.cn.osiris.basic.jwt.JwtUtils;
import org.cn.osiris.basic.jwt.LoginData;
import org.cn.osiris.basic.jwt.RsaUtils;
import org.cn.osiris.basic.util.AjaxResult;
import org.cn.osiris.basic.util.HttpUtil;
import org.cn.osiris.basic.util.Md5Utils;
import org.cn.osiris.basic.util.StrUtils;
import org.cn.osiris.org.domain.Employee;
import org.cn.osiris.org.mapper.EmployeeMapper;
import org.cn.osiris.system.domain.Menu;
import org.cn.osiris.user.domain.Logininfo;
import org.cn.osiris.user.domain.User;
import org.cn.osiris.user.domain.WxConstants;
import org.cn.osiris.user.domain.Wxuser;
import org.cn.osiris.user.mapper.LogininfoMapper;
import org.cn.osiris.user.mapper.UserMapper;
import org.cn.osiris.user.mapper.WxuserMapper;
import org.cn.osiris.user.service.ILogininfoService;
import org.cn.osiris.basic.service.impl.BaseServiceImpl;
import org.cn.osiris.user.service.IUserService;
import org.cn.osiris.user.service.IWxuserService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.security.PrivateKey;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 业务实现类：
 */
@Service
public class LogininfoServiceImpl extends BaseServiceImpl<Logininfo> implements ILogininfoService {

    @Autowired
    private LogininfoMapper logininfoMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private WxuserMapper wxuserMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private EmployeeMapper employeeMapper;

    /**
     *账号登录
     * @param loginDto
     * @return
     *
     * 接收数据 ---首先进行数据检验
     *  空值校验、账号校验、密码校验
     *  然后再对 启用状态进行校验
     *
     *  生成token，并将登陆信息保存到redis数据库中,设置30有效（超过三十分钟为操作自动退出，需要重新登录）
     *
     *  然后返回带有token和登录信息的map集合
     */
    @Override
    public Map<String, Object> accountLogin(LoginDto loginDto) {
        //拿取接收到的数据
        //账号
        String account = loginDto.getAccount();
        System.out.println(account);
        //密码
        String checkPass = loginDto.getCheckPass();
        //空值校验
        if (StringUtils.isEmpty(account)||StringUtils.isEmpty(checkPass)){
            throw new BusinessException("请检查，数据不能为空");
        }

        //账号校验：首先要查询数据库中是否存在该账号
        Logininfo logininfo = logininfoMapper.findByAccount(loginDto);
        System.out.println(logininfo);
        if (logininfo ==null){
            throw  new BusinessException("账号或密码错误！！！");
        }

        //密码校验 因为之前注册时，密码进行了加密加盐
        String inputPwd = Md5Utils.encrypByMd5(checkPass + logininfo.getSalt());
        if (!inputPwd.equals(logininfo.getPassword())){
            throw  new BusinessException("账号或密码错误！！！");
        }

        //校验启用状态 是否被禁用
        if (!logininfo.getDisable()){
            throw new BusinessException("该账号已被禁用，请联系平台管理人员进行解决");
        }

        //生成token,并将我们的登录信息一起保存到redis数据库，设置30有效
       /* //通过UUID生成随机的36为的token字符串
        String token = UUID.randomUUID().toString();
        //然后将数据保存到redis数据库，设置30有效
        redisTemplate.opsForValue().set(token,logininfo,30, TimeUnit.MINUTES);


        //将需要返回的数据保存到map中
        HashMap<String, Object> map = new HashMap<>();
        map.put("token",token);
        //保存登录信息前，我们需要将盐值和密码进行置空，防止用户的账号密码泄露
        logininfo.setSalt(null);
        logininfo.setPassword(null);
        map.put("logininfo",logininfo);*/
        Map<String, Object> map = LoginSuccessJwtHandler(logininfo);

        //返回添加了token和登录信息的map
        return map;
    }

    /**微信登录
     *更具根据code发送的第二请求：httputils返回一个json字符串
     * 准备第二个请求和参数
     * 使用fastjson进行处理；获得token和openid
     * 然后根据openid查询到微信对应的对象
     *
     * */
    @Override
    public AjaxResult wechatLogin(String code) {
        //根据code发送第二个请求，httputils返回json字符串
        //准备第二个请求和参数
        String url = WxConstants.GET_ACK_URL
                .replace("APPID", WxConstants.APPID)
                .replace("SECRET", WxConstants.SECRET)
                .replace("CODE", code);
        //调用工具类发送请求，并返回json格式字符串
        String jsonStr = HttpUtil.httpGet(url);

        //处理json格式的字符串，拿取到token和openid
        JSONObject obj = JSONObject.parseObject(jsonStr);

        String access_token = obj.getString("access_token");
        String openid = obj.getString("openid");

        //根据openid去查询t_wxuser表
         Wxuser wxuser = wxuserMapper.findByOpenid(openid);
        System.out.println(wxuser);
        /*
         *如果查询到了 说明已经扫码绑定过 可以直接授权登录
         * 如果没有查到，则需要重新绑定   那么就会响应给前端token，openid，并且跳转到绑定页面
         */

         if (wxuser!=null){
             //疑问：user_id可能为null吗？只要有wxuser，就一定会绑定user_id
             User user = userMapper.findById(wxuser.getUserId());
             if (user!=null){
                 //查找关联的对象的值
                 Logininfo logininfo = logininfoMapper.findById(user.getLogininfoId());

                 /*//通过uuid设置随机字符串的token
                 String token = UUID.randomUUID().toString();
                 //并设置有效时间
                 redisTemplate.opsForValue().set(token,logininfo,30,TimeUnit.MINUTES);

                 //将返回的值封装到map中：Map【token，logininfo】
                 Map<String, Object> map = new HashMap<>();
                 map.put("token",token);
                 //为了防止用户信息泄露，将盐值和密码置空
                 logininfo.setSalt(null);
                 logininfo.setPassword(null);
                 map.put("logininfo",logininfo);*/
                 Map<String, Object> map = LoginSuccessJwtHandler(logininfo);

                 return AjaxResult.me().setResultObj(map);
             }
         }
        //如果没有查找到响应给前端token和openid
        System.out.println(AjaxResult.me().setSuccess(false)
                .setResultObj("?access_token="+access_token+"&openid="+openid));
        return AjaxResult.me().setSuccess(false)
                .setResultObj("?access_token="+access_token+"&openid="+openid);
    }

    /**
     * 微信接口绑定手机号
     * */
    @Override
    public AjaxResult wechatBinder(Map<String, String> map) {
        String phone = map.get("phone");
        String verifyCode = map.get("verifyCode");
        String accessToken = map.get("accessToken");
        String openId = map.get("openId");

        //一：校验：空值校验
        if(StringUtils.isEmpty(phone) || StringUtils.isEmpty(verifyCode)){
            throw new BusinessException("信息不能为空!!!");
        }

        //校验：绑定是否失效 - 防止直接访问http://bugtracker.itsource.cn/binder.html
        if(StringUtils.isEmpty(accessToken) || StringUtils.isEmpty(openId)){
            throw new BusinessException("绑定失效，请重新扫码!!!");
        }

        //二：校验：手机验证码校验 - 是否过期，是否错误
        //key-value = binder:phone-code:时间戳
        Object phoneCode = redisTemplate.opsForValue().get("binder:" + phone);
        if(phoneCode==null){
            throw new BusinessException("手机验证码过期!!!");
        }
        if(!phoneCode.toString().split(":")[0].equals(verifyCode)){
            throw  new BusinessException("手机验证码错误!!!");
        }

        //三：组装第三个请求的url地址
        String url = WxConstants.GET_USER_URL.replace("ACCESS_TOKEN", accessToken).
                replace("OPENID", openId);
        //四：发送http请求，并返回json格式的字符串
        String jsonStr = HttpUtil.httpGet(url);

        //五：将json格式的字符串转换成json对象，并转换成一个wxuser对象
        Wxuser wxuser = jsonStr2Wxuser(jsonStr);

        //六：根据电话获取【以前注册过但是没有和微信绑定】或新建user对象【logininfo对象添加】，并将User对象添加到数据库
        User user = userMapper.findByPhone(phone);
        if(user==null){//已经这个电话没有被注册过 = 新建
            //先创建user对象
            user = phone2User(phone);
            //再创建logininfo
            Logininfo logininfo = user2Logininfo(user);

            //先保存logininfo对象
            logininfoMapper.add(logininfo);
            user.setLogininfoId(logininfo.getId());
            //再添加user对象
            userMapper.add(user);
        }

        //七：将user_id设置到wxuser对象中，并将wxuser添加到数据库
        wxuser.setUserId(user.getId());
        wxuserMapper.add(wxuser);

        //八：免密登录
        Logininfo logininfoTemp = logininfoMapper.findById(user.getLogininfoId());

        //使用jwtToken将用户信息加密，返回给前端
        Map<String, Object> mapTemp = LoginSuccessJwtHandler(logininfoTemp);

        //响应数据给前端
        return AjaxResult.me().setResultObj(mapTemp);
    }


    /**
     * JWT
     * @param logininfo
     * @return
     * 流程：
     *  1、登录成功之后将登录信息加密程jwttoken字符串
     *  2、将jwt token字符串传递给前端保存起来 前端保存在locaStorage
     *  3、后续每次发送请求都在请求头中携带token
     *  4、再拦截其中获取搭配token，拿到解密数据
     *  5、做判断
     *
     */
    private Map<String,Object> LoginSuccessJwtHandler(Logininfo logininfo){
        //目的：读取四个数据
        HashMap<String, Object> map = new HashMap<>();

        LoginData loginData = new LoginData();
        map.put("loginData",loginData);
        loginData.setLogininfo(logininfo);

        //置空返回给前端数据中的盐值和密码
        logininfo.setSalt(null);
        logininfo.setPassword(null);
        if (logininfo.getType()==0){
            //获取权限
            List<String> permissions = employeeMapper.getSns(logininfo.getId());
            map.put("permissions",permissions);
            loginData.setPermissions(permissions);
            //获取当前登陆人菜单
            List<Menu> menus = employeeMapper.getMenus(logininfo.getId());
            map.put("menus",menus);
            loginData.setMenus(menus);

        }
        try {
            //使用私钥加密
            PrivateKey privateKey = RsaUtils.getPrivateKey(LogininfoServiceImpl.class.getClassLoader().getResource("auth_rsa.pri").getFile());
            //组装、设置过期时间
            String token = JwtUtils.generateTokenExpireInMinutes(loginData, privateKey, 30);
            //添加到map
            map.put("token",token);
        }catch (Exception e){
            e.printStackTrace();
        }

        return map;
    }

    private Logininfo user2Logininfo(User user) {
        Logininfo logininfo = new Logininfo();
        BeanUtils.copyProperties(user,logininfo);
        logininfo.setType(1);
        logininfo.setDisable(true);
        return logininfo;
    }

    private User phone2User(String phone) {
        User user = new User();
        user.setUsername(phone);
        user.setPhone(phone);
        String salt = StrUtils.getComplexRandomString(32);
        //给一个随机密码，可以发短信或发邮箱告诉他。也可以不告诉他 - 后面都是用微信app扫码 - 不会涉及到密码
        String randomPwd = StrUtils.getComplexRandomString(6);
        String md5Pwd = Md5Utils.encrypByMd5(randomPwd + salt);
        user.setSalt(salt);
        user.setPassword(md5Pwd);
        user.setState(1);
        return user;
    }

    private Wxuser jsonStr2Wxuser(String jsonStr) {
        JSONObject obj = JSONObject.parseObject(jsonStr);
        Wxuser wxuser = new Wxuser();
        wxuser.setOpenid(obj.getString("openid"));
        wxuser.setNickname(obj.getString("nickname"));
        //现在微信已经将性别
        wxuser.setSex(Integer.parseInt(obj.getString("sex")));
        //地址：country + province + city
        wxuser.setHeadimgurl(obj.getString("headimgurl"));
        wxuser.setUnionid(obj.getString("unionid"));

        return wxuser;
        /*
        *{
        "openid":"OPENID",
        "nickname":"NICKNAME",
        "sex":1,
        "province":"PROVINCE",
        "city":"CITY",
        "country":"COUNTRY",
        "headimgurl": "https://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/0",
        "privilege":[
        "PRIVILEGE1",
        "PRIVILEGE2"
        ],
        "unionid": " o6_bmasdasdsad6_2sgVt7hMZOPfL"`
        }
        * */
    }
}
